package com.liuceng.zen.web.auth.realm;

import com.alibaba.fastjson.JSONObject;
import com.github.kevinsawicki.http.HttpRequest;
import com.liuceng.zen.common.utils.Encodes;
import com.liuceng.zen.pojo.domain.Member;
import com.liuceng.zen.service.AccountService;
import com.liuceng.zen.service.ImageService;
import com.liuceng.zen.service.MemberService;
import com.liuceng.zen.web.auth.UsernamePasswordToken;
import lombok.Data;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.subject.support.WebDelegatingSubject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class ShiroDbRealm extends AuthorizingRealm {
  private AccountService accountService;
  @Autowired
  private MemberService memberService;
  @Autowired
  ImageService imageService;
  //人机验证key
  @Value("${api_key}")
  private String api_key;
  //人机验证url
  @Value("${luosimao_url}")
  private String luosimao_url;
  
  /**
   * 认证回调函数,登录时调用.
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
      throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
    String res = getLuotestResponse(token.getLuosimao());//人机登录返回结果
    //
    if (!UsernamePasswordToken.FLAG_AUTOLOGIN.equals(token.getFlag())) {//不是自动登录需验证码
      if (null == res || !res.equals("success")) {//验证人机验证是否成功
        return null;
      }
    }
    if (UsernamePasswordToken.FLAG_AUTOLOGIN.equals(token.getFlag())) {
      return null;
    }
    if (!token.getIsWeChatLogin()) {//不是扫码登录需要验证码
      if (null == res || !res.equals("success")) {//验证人机验证是否成功
        return null;
      }
    }
    if (token.getIsWeChatLogin()) {
      return null;
    }
    WebDelegatingSubject subject = (WebDelegatingSubject) SecurityUtils.getSubject();

    Member member = memberService.selectByuserName(token.getUsername());
    if (member != null) {
      byte[] salt = Encodes.decodeHex(member.getSalt());
      return new SimpleAuthenticationInfo(new UserContext(member), member.getPassword(),
          ByteSource.Util.bytes(salt), getName());
    } else {
      return null;
    }
  }

  /**
   * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    UserContext userContext = (UserContext) getAvailablePrincipal(principals);
    Member user = accountService.getMemberByUsername(userContext.getUsername());
    if (user != null) {
      SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
      return authorizationInfo;
    }
    return null;
  }

  /**
   * 设定Password校验的Hash算法与迭代次数.
   */
  @PostConstruct
  public void initCredentialsMatcher() {
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(AccountService.HASH_ALGORITHM);
    matcher.setHashIterations(AccountService.HASH_INTERATIONS);
    setCredentialsMatcher(matcher);
  }

  /**
   * 清空用户关联权限认证，待下次使用时重新加载
   */
  public void clearCachedAuthorizationInfo(String principal) {
    SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
    clearCachedAuthorizationInfo(principals);
  }

  /**
   * 清空所有关联认证
   */
  public void clearAllCachedAuthorizationInfo() {
    Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
    if (cache != null) {
      for (Object key : cache.keys()) {
        cache.remove(key);
      }
    }
  }

  /**
   * 授权用户信息
   */
  @Data
  public static class UserContext implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer id;
    private String username;
    private String email;
    private Member member;
    private Map<String, Object> cacheMap;
    private boolean fromBackend;

    public UserContext(Member member) {
      this(member, false);
    }

    public UserContext(Member member, boolean fromBackend) {
      this.id = member.getId();
      this.member = member;
      this.username = member.getUsername();
      this.email = member.getEmail();
      this.fromBackend = fromBackend;
    }

    public Map<String, Object> getCacheMap() {
      if (cacheMap == null) {
        cacheMap = new HashMap<String, Object>();
      }
      return cacheMap;
    }
  }

  public void setAccountService(AccountService accountService) {
    this.accountService = accountService;
  }

  //整理人机验证验证返回结果的参数
  protected Object[] map2arr(Map<String, Object> params) {
    if (params == null || params.isEmpty()) {
      return null;
    }
    Object[] keys = params.keySet().toArray();
    Object[] vals = params.values().toArray();
    List<Object> objList = new ArrayList<>();
    for (int i = 0; i < keys.length; i++) {
      Object key = keys[i];
      Object val = vals[i];
      if (val != null) {
        objList.add(key);
        objList.add(val);
      }
    }
    return objList.toArray();
  }


  //获取人机登录返回结果
  protected String getLuotestResponse(String response) {
    Map<String, Object> params = new HashMap<>();
    params.put("api_key",api_key);
    params.put("response", response);
    HttpRequest req = HttpRequest.post(luosimao_url,true,map2arr(params));
    String content = req.body();
    JSONObject json = JSONObject.parseObject(content);
    return json.getString("res");
  }
}
